package com.kmxd.ams.app.arrange.command;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.kmxd.ams.client.archive.vo.ArchiveFieldListVO;
import com.kmxd.ams.client.arrange.IDocumentSvc;
import com.kmxd.ams.client.arrange.IVolumeSvc;
import com.kmxd.ams.client.arrange.dto.RefFieldDTO;
import com.kmxd.ams.client.arrange.dto.UploadImportDataDTO;
import com.kmxd.ams.core.common.constant.Constants;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.exception.ReturnDataException;
import com.kmxd.ams.core.oss.template.OssTemplate;
import com.kmxd.ams.core.util.DynamicTableNameUtil;
import com.kmxd.ams.infra.arrange.entity.Document;
import com.kmxd.ams.infra.arrange.entity.Volume;
import java.util.*;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 导入数据校验
 *
 * @author zuolg
 */
@AllArgsConstructor
@Component
@Slf4j
public class ImportDateCheckCmd {
  private final OssTemplate ossTemplate;

  /**
   * 判断重复文件 1、判断文件自身是否重复 2、判断文件与数据库是否重复
   *
   * @param req
   * @throws ReturnDataException
   */
  public void checkRepeatDoc(UploadImportDataDTO req) throws ReturnDataException {
    List<String> excelFields =
        req.getRefField().stream()
            .filter(x -> ObjectUtil.isNotEmpty(x.getExcelField()))
            .map(RefFieldDTO::getExcelField)
            .collect(Collectors.toList());

    List<Map<String, Object>> excelData = ListUtils.newArrayList();
    // 读取Excel信息
    EasyExcel.read(
            req.getPath(),
            new ReadListener<Map<Integer, String>>() {
              Map<Integer, ReadCellData<?>> headMaps;

              @Override
              public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
                // 读取到的每行数据,其key是以0开始的索引
                Map<String, Object> rowMap = MapUtil.newHashMap();
                rowData.forEach(
                    (key, value) -> {
                      ReadCellData cellData = MapUtil.get(headMaps, key, ReadCellData.class);
                      String cnKey =
                          ObjectUtil.isNotEmpty(cellData) ? cellData.getStringValue() : null;
                      rowMap.put(cnKey, value);
                    });
                excelData.add(rowMap);
              }

              @Override
              public void invokeHead(
                  Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                headMaps = headMap;
              }

              @Override
              public void doAfterAllAnalysed(AnalysisContext context) {
                log.info("数据读取完成！");
              }
            })
        .sheet()
        .doRead();

    log.info("=========" + excelData.toString());
    // 判断Excel本身数据是否重复
    List<Map<String, Object>> duplicates = getDuplicates(excelData, req.getDidExcel());
    // excel数据重复
    if (!duplicates.isEmpty()) {
      String pathYear = DateUtil.format(DateUtil.date(), "yyyy/MM/dd");
      String fileName =
          StrUtil.format(
              "数据导入_{}.xlsx", DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN));
      String path = ossTemplate.getTempPath() + ossTemplate.separator() + pathYear;
      String filePath = path + ossTemplate.separator() + fileName;
      FileUtil.mkdir(path);
      creatExcel(excelFields, duplicates, "文件重复", filePath);
      throw ReturnDataException.instance(ErrorCode.ARCHIVE_DATA_EXIST, filePath);
    }

    List<Object> qrDatas =
        excelData.stream()
            .map(
                item -> {
                  return MapUtil.get(item, req.getDidExcel(), Object.class, null);
                })
            .collect(Collectors.toList());

    if (!qrDatas.isEmpty()) {
      DynamicTableNameUtil.setTableName(req.getTableId());
      IDocumentSvc documentSvc = SpringUtil.getBean(IDocumentSvc.class);
      int limit = Constants.DEF_BATCH_SAVE_SIZE * 4;
      List<Document> documentList = Lists.newArrayList();
      int totalPage = PageUtil.totalPage(qrDatas.size(), limit);
      for (int i = 1; i <= totalPage; i++) {
        List<Object> qrDataTemp =
            qrDatas.stream()
                .skip(Constants.DEF_BATCH_SAVE_SIZE * (i - 1))
                .limit(limit)
                .collect(Collectors.toList());
        LambdaQueryWrapper<Document> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.in(Document::getDid, qrDataTemp);
        List<Document> documentTemp = documentSvc.list(queryWrapper);
        documentList.addAll(documentTemp);
      }

      if (!documentList.isEmpty()) {
        List<String> didData =
            documentList.stream().map(Document::getDid).collect(Collectors.toList());
        String pathYear = DateUtil.format(DateUtil.date(), "yyyy/MM/dd");
        String fileName =
            StrUtil.format(
                "数据导入_{}.xlsx",
                DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN));
        String path = ossTemplate.getTempPath() + ossTemplate.separator() + pathYear;
        String filePath = path + ossTemplate.separator() + fileName;
        FileUtil.mkdir(path);
        creatExcel(
            excelFields, getDuplicates(excelData, didData, req.getDidExcel()), "数据库对比异常", filePath);
        throw ReturnDataException.instance(ErrorCode.ARCHIVE_DATA_EXIST, filePath);
      }
    }
  }

  /**
   * 判断案卷是否重复
   *
   * @param req
   * @throws ReturnDataException
   */
  public void checkRepeatVol(UploadImportDataDTO req) throws ReturnDataException {
    List<String> excelFields =
        req.getRefField().stream()
            .filter(x -> ObjectUtil.isNotEmpty(x.getExcelField()))
            .map(RefFieldDTO::getExcelField)
            .collect(Collectors.toList());

    List<Map<String, Object>> excelData = ListUtils.newArrayList();
    // 读取Excel信息
    EasyExcel.read(
            req.getPath(),
            new ReadListener<Map<Integer, String>>() {
              Map<Integer, ReadCellData<?>> headMaps;

              @Override
              public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
                // 读取到的每行数据,其key是以0开始的索引
                Map<String, Object> rowMap = MapUtil.newHashMap();
                rowData.forEach(
                    (key, value) -> {
                      ReadCellData cellData = MapUtil.get(headMaps, key, ReadCellData.class);
                      String cnKey =
                          ObjectUtil.isNotEmpty(cellData) ? cellData.getStringValue() : null;
                      rowMap.put(cnKey, value);
                    });
                excelData.add(rowMap);
              }

              @Override
              public void invokeHead(
                  Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                headMaps = headMap;
              }

              @Override
              public void doAfterAllAnalysed(AnalysisContext context) {
                log.info("数据读取完成！");
              }
            })
        .sheet()
        .doRead();

    log.info("=========" + excelData.toString());
    // 判断Excel本身数据是否重复
    List<Map<String, Object>> duplicates = getDuplicates(excelData, req.getDidExcel());
    // excel数据重复
    if (!duplicates.isEmpty()) {
      String pathYear = DateUtil.format(DateUtil.date(), "yyyy/MM/dd");
      String fileName =
          StrUtil.format(
              "数据导入_{}.xlsx", DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN));
      String path = ossTemplate.getTempPath() + ossTemplate.separator() + pathYear;
      String filePath = path + ossTemplate.separator() + fileName;
      FileUtil.mkdir(path);
      creatExcel(excelFields, duplicates, "文件重复", filePath);
      throw ReturnDataException.instance(ErrorCode.ARCHIVE_DATA_EXIST, filePath);
    }

    List<Object> qrDatas =
        excelData.stream()
            .map(
                item -> {
                  return MapUtil.get(item, req.getDidExcel(), Object.class, null);
                })
            .collect(Collectors.toList());

    if (!qrDatas.isEmpty()) {
      DynamicTableNameUtil.setTableName(req.getTableId());
      IVolumeSvc volumeSvc = SpringUtil.getBean(IVolumeSvc.class);
      int limit = Constants.DEF_BATCH_SAVE_SIZE * 4;
      List<Volume> volList = Lists.newArrayList();
      int totalPage = PageUtil.totalPage(qrDatas.size(), limit);
      for (int i = 1; i <= totalPage; i++) {
        List<Object> qrDataTemp =
            qrDatas.stream()
                .skip(Constants.DEF_BATCH_SAVE_SIZE * (i - 1))
                .limit(limit)
                .collect(Collectors.toList());
        LambdaQueryWrapper<Volume> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.in(Volume::getDid, qrDataTemp);
        List<Volume> volListTmp = volumeSvc.list(queryWrapper);
        volList.addAll(volListTmp);
      }

      if (!volList.isEmpty()) {
        List<String> didData = volList.stream().map(Volume::getDid).collect(Collectors.toList());
        String pathYear = DateUtil.format(DateUtil.date(), "yyyy/MM/dd");
        String fileName =
            StrUtil.format(
                "卷数据导入_{}.xlsx",
                DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN));
        String path = ossTemplate.getTempPath() + ossTemplate.separator() + pathYear;
        String filePath = path + ossTemplate.separator() + fileName;
        FileUtil.mkdir(path);
        creatExcel(
            excelFields, getDuplicates(excelData, didData, req.getDidExcel()), "数据库对比异常", filePath);
        throw ReturnDataException.instance(ErrorCode.ARCHIVE_DATA_EXIST, filePath);
      }
    }
  }

  /**
   * 生成Excel
   *
   * @param excelFields
   * @param duplicates
   * @param filePath
   */
  private void creatExcel(
      List<String> excelFields,
      List<Map<String, Object>> duplicates,
      String sheetName,
      String filePath) {
    List<List<String>> headList = Lists.newArrayList();
    for (String excelField : excelFields) {
      List<String> titleLists = Lists.newArrayList(excelField.toString());
      headList.add(titleLists);
    }
    EasyExcel.write(filePath)
        .head(headList)
        .sheet(StrUtil.isNotBlank(sheetName) ? sheetName : "数据异常")
        .doWrite(map2ExcelData(excelFields, duplicates));
  }

  /**
   * 数据转Excel数据
   *
   * @param duplicates
   * @return
   */
  private List<List<Object>> map2ExcelData(
      List<String> excelFields, List<Map<String, Object>> duplicates) {
    List<List<Object>> list = ListUtils.newArrayList();

    for (Map<String, Object> map : duplicates) {
      List<Object> values = Lists.newArrayList();
      for (String field : excelFields) {
        values.add(MapUtil.getStr(map, field, ""));
      }
      list.add(values);
    }
    return list;
  }

  public String getKeyEn(String keyCn, List<ArchiveFieldListVO> fields) {
    List<ArchiveFieldListVO> field =
        fields.stream()
            .filter(item -> item.getDbFieldName().equalsIgnoreCase(keyCn))
            .collect(Collectors.toList());
    return field.size() > 0 ? StringUtils.underlineToCamel(field.get(0).getDbFieldKey()) : keyCn;
  }

  /**
   * 获取excel对象数据
   *
   * @param excelData
   * @param didData
   * @param didCn
   * @return
   * @param <T>
   */
  public static <T> List<Map<String, Object>> getDuplicates(
      List<Map<String, Object>> excelData, List<String> didData, String didCn) {
    List<Map<String, Object>> returnData = Lists.newArrayList();
    List<Map<String, Object>> duplicates = new ArrayList<>();
    for (Map<String, Object> map : excelData) {
      String didValue = MapUtil.getStr(map, didCn);
      if (didData.contains(didValue)) {
        returnData.add(map);
      }
    }
    return returnData;
  }

  /**
   * 判断重复
   *
   * @param list
   * @return
   * @param <T>
   */
  public static <T> List<Map<String, Object>> getDuplicates(
      List<Map<String, Object>> list, String didCn) {
    Set<String> set = new HashSet<>();
    List<Map<String, Object>> duplicates = new ArrayList<>();
    for (Map<String, Object> map : list) {
      String didValue = MapUtil.getStr(map, didCn);
      if (!set.add(didValue)) {
        duplicates.add(map);
      }
    }
    return duplicates;
  }
}
